package mmc;

import static org.lwjgl.opengl.ARBPointParameters.GL_POINT_DISTANCE_ATTENUATION_ARB;
import static org.lwjgl.opengl.ARBPointParameters.GL_POINT_SIZE_MAX_ARB;
import static org.lwjgl.opengl.ARBPointParameters.GL_POINT_SIZE_MIN_ARB;
import static org.lwjgl.opengl.ARBPointParameters.glPointParameterARB;
import static org.lwjgl.opengl.ARBPointParameters.glPointParameterfARB;
import static org.lwjgl.opengl.ARBPointSprite.GL_COORD_REPLACE_ARB;
import static org.lwjgl.opengl.ARBPointSprite.GL_POINT_SPRITE_ARB;
import static org.lwjgl.opengl.GL11.*;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.net.Socket;

import javax.swing.JFrame;

import mmc.networking.Quit;
import mmc.networking.RequestInitialScene;
import mmc.resource.TextureLoader;
import mmc.ui.UI;
import mmc.util.APSCounter;
import mmc.util.Utils;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.SharedDrawable;
import org.lwjgl.util.glu.GLU;


public class Client {
	private Scene scene;
	public boolean closeRequested = false;
	private Quit.QuitCause quitCause = null;
	private boolean resize = false;
	private JFrame frame;
	private Canvas canvas;
	private APSCounter fps = new APSCounter("fps");
	private RemoteServer server;
	public UI ui;
	public static Client client;
	
	public Client(String playerName, Socket socket) throws LWJGLException
	{
		client = this;
		
		try {
			server = new RemoteServer(this, socket);
		} catch (IOException e1) {
			e1.printStackTrace();
		}
		
		openWindow(playerName);
	}
	
	public Client(String playerName, String host, int port) throws LWJGLException{
		client = this;
		
		try {
			server = new RemoteServer(this, host, port);
		} catch (IOException e1) {
			throw new RuntimeException("Coult not connect to server: "+e1.getMessage(),e1);
		}
		
		openWindow(playerName);
	}
	
	private void openWindow(String playerName) throws LWJGLException{
		frame = new JFrame("MMC");
		canvas = new Canvas();
		canvas.setPreferredSize(new Dimension(800,600));
		frame.getContentPane().add(canvas);
		frame.setLocation(100, 100);
		frame.setVisible(true);
		frame.pack();
		
		init();
		frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
		frame.addWindowListener(new WindowAdapter(){
			@Override public void windowClosing(WindowEvent arg0) {
				closeRequested = true;
			}});
		frame.addComponentListener(new ComponentAdapter(){  
			@Override public void componentResized(ComponentEvent e) {
				resize = true;
			}
			@Override public void componentShown(ComponentEvent arg0) {}
		});
		
		//new Engine(scene = new Scene());
		server.post(new RequestInitialScene(playerName));
		
		mainLoop();
	}
	
	private void init() throws LWJGLException{
		int width = (int)canvas.getSize().getWidth(), height = (int)canvas.getSize().getHeight();
		
		//Workaround for freezes in Ubuntu/Unity
		while(width==1&&height==1){
			System.out.println("WARNING: applied workaround");
			try {Thread.sleep(100);} catch (InterruptedException e) {}
			width = (int)canvas.getSize().getWidth();
			height = (int)canvas.getSize().getHeight();
		}
		
		System.out.println("init "+width+"x"+height);
		
		Display.setDisplayMode(new DisplayMode(width, height));
		Display.setParent(canvas);
		Display.create();
		Display.setVSyncEnabled(false);
		
		glClearColor(0f, 0f, 0f, 0f);
		
		glEnable(GL_TEXTURE_2D);
		
		glEnable(GL_DEPTH_TEST);
		glDepthFunc(GL_LEQUAL);
		
		/*
		glEnable(GL_CULL_FACE);
		glFrontFace(GL_CW);
		*/
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
		glViewport(0, 0, width, height);
		
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		GLU.gluPerspective(Constants.FOV,((float)width)/((float)height),0.1f,Constants.RENDER_DISTANCE);		
		
		glEnable(GL_LIGHTING);
		glEnable(GL_AMBIENT_AND_DIFFUSE);
		glShadeModel(GL_SMOOTH);
		
		//Point sprites
		glPointParameterARB(GL_POINT_DISTANCE_ATTENUATION_ARB, Utils.wrap4f(0,0,0.0001f,0));
        glPointParameterfARB(GL_POINT_SIZE_MIN_ARB, 1.0f);
        glPointParameterfARB(GL_POINT_SIZE_MAX_ARB, 100.0f);
        glTexEnvi(GL_POINT_SPRITE_ARB, GL_COORD_REPLACE_ARB, 1);
		
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		
		ui = new UI(canvas, this);		
		
		/**
		 * precache classes (static initializers with gl code can do damage when first instantiated from a nongl thread)
		 */
		try {
			Class.forName("mmc.objects.Bomb");
			Class.forName("mmc.objects.Mark");
			Class.forName("mmc.objects.Mob");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
	}
	
	private void mainLoop() throws LWJGLException{
		System.out.println("MainLoop starting...");
		long lastUpdate = System.nanoTime()-1;
		long now;
		float dt;
		
		try{
			while(! closeRequested){
				if(resize){
					resize = false;
					System.out.println("resizing");
					
					int width = (int)canvas.getSize().getWidth(), height = (int)canvas.getSize().getHeight();
					Display.setDisplayMode(new DisplayMode(width, height));
					glViewport(0, 0, width, height);
					glMatrixMode(GL_PROJECTION);
					glLoadIdentity();
					GLU.gluPerspective(Constants.FOV,((float)width)/((float)height),0.1f,Constants.RENDER_DISTANCE);
					glMatrixMode(GL_MODELVIEW);
					glLoadIdentity();
				}
				
				glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
				
				glMatrixMode(GL_MODELVIEW);
				glLoadIdentity();
				
				if(scene!=null)
					scene.render(null);
				
				now = System.nanoTime();
				dt = (float)((now - lastUpdate)*1e-9);
				if(scene!=null)
					scene.update(dt);
				lastUpdate = now;
				

				ui.render();
				Display.update();
				
				fps.notify(dt, 1);
			}
		}finally{
			System.out.println("cleanup");
			if(scene!=null)
				scene.shutdown();
			TextureLoader.clearTextureCache();
			Display.destroy();
			frame.dispose();
			server.quit();
			ui.safeChangesOnClose();
			
			System.out.println("QC = "+quitCause);
			
			if(!Quit.QuitCause.CLIENT_SHUTDOWN.toString().equals(quitCause.toString())){
				Launcher l = new Launcher(scene.getPlayer().getRemotePlayer().getName(), server.socket.getInetAddress().getHostAddress(), server.socket.getPort());
				l.l_status.setText(quitCause.toString());
			}
		}
	}
	
	public static void main(String[]args) throws LWJGLException{
		if(args.length==2)
			new Client(args[0],args[1],4711);
		else if(args.length==3)
			new Client(args[0],args[1],new Integer(args[2]));
		else
			System.out.println("usage: mmcid <loginName> <host> [port]");
	}
	
	public Scene getScene(){
		return scene;
	}
	
	public void setScene(Scene scene){
		this.scene = scene;
	}
	
	public Dimension getSize(){
		return canvas.getSize();
	}
	
	public APSCounter getFPS(){
		return fps;
	}
	
	public void setQuitCause(Quit.QuitCause qcause){
		if(quitCause==null) //first = best
			quitCause = qcause;
	}
	
	public boolean isQuitting(){
		return quitCause!=null;
	}
	
	public SharedDrawable createSharedDrawable(){
		try {
			return new SharedDrawable(Display.getDrawable());
		} catch (LWJGLException e) {
			throw new RuntimeException("Error while creating SharedDrawable: "+e.getMessage(),e);
		}
	}
}
